/*
 * Copyright (c) 2021 Huawei Device Co.,  Ltd.
 * Licensed under the Apache License,  Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,  software
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,  either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "printertask.h"
#include <cstddef>
#include <chrono> 
#include <unistd.h>
#include <thread>
#include "recptprinter_hilog.h"
#include "iprinteradaptor.h"
#include "HWISPRTPrinter.h"
#include "recptprinter_hilog.h"
#include "charset.h"

namespace SwanLink {
namespace Finance {

#define CHECK_STATUS_NO_RET(cond, status) do {\
    if (!(cond)) {\
        status_ = SUCCESS;\
    } else {\
        status_ = status;\
        return;\
    }\
}while(0)

int StatusConvert(int status) {
    switch (status) {
    case ERR_PERMISSION_DENY: return ERR_DEVICE_PERMISSION_DENY;
    case ERR_NOT_FOUND: return ERR_DEVICE_NOT_EXIST;
    case ERR_DEV_NOT_FOUND: return ERR_DEVICE_NO_DEVBUS;
    case ERR_DRVIVER_ERROR: return ERR_DEVICE_DRIVER_ERROR;
    case ERR_OPEN_FAILED: return ERR_DEVICE_OPEN_FAILED;
    case ERR_NOT_OPEN: return ERR_DEVICE_NOT_OPEN;
    case ERR_SEND_FAILED: return ERR_TRANS_SEND_FAILED;
    case ERR_SEND_UNFINISHED: return ERR_TRANS_SEND_UNFINISHED;
    case ERR_RECV_FAILED: return ERR_TRANS_RECV_FAILED;
    case ERR_RECV_UNFINISHED: return ERR_TRANS_RECV_UNFINISHED;
    case ERR_OFFLINE: return ERR_PRINTER_OUT_OF_PAPER;
    case ERR_ONLINE_ERROR: return ERR_PRINTER_ONLINE_ERROR;
    case ERR_FEEDPAPER_BROKEN: return ERR_PRINTER_CUT_BROKEN;
    case ERR_PAPER_USEDOUT: return ERR_PRINTER_OUT_OF_PAPER;
    case ERR_CUT_ERROR: return ERR_PRINTER_CUT_ERROR;
    case ERR_OVERHAET: return ERR_PRINTER_OVERHEATING;
    default: {
        if (status != 0) {
            return ERR_PRINTER_HW_ERROR;
        }
    }
    }
    return status;
}

PrinterTask::PrinterTask():
    thread_id_(0),
    run_(false),
    status_(ERR_PRINTER_NO_TASK),
    listener_(nullptr) {
}

PrinterTask::~PrinterTask() {
    do {
        std::lock_guard<std::mutex> lock(mutex_);
        run_ = false;
    } while (false);
    if (thread_id_ != 0) {
        pthread_join(thread_id_, NULL);
    }
    thread_id_ = 0;
}

int PrinterTask::AddTask(PrintTaskType ppt, int x, int y, PrintFormat  printFormat, const std::string& content)
{
    std::lock_guard<std::mutex> lock(mutex_);
    if (run_) {
        status_ = ERR_PRINTER_BUSY;
        return status_;
    }
    PrintItem task;
    task.ppt = ppt;
    task.x = x;
    task.y = y;
    task.fmt = printFormat;
    task.content = content;
    queTasks_.push(task);
    return SUCCESS;
}

int PrinterTask::StartPrint(int timeout, PrintListener* listener)
{
    do {
        std::lock_guard<std::mutex> lock(mutex_);
        if (run_) {
            status_ = ERR_PRINTER_BUSY;
            return status_;
        }
    }while(0);
    listener_ = listener;

    pthread_create(&thread_id_, NULL, RunTaskThread, this);
    pthread_join(thread_id_, NULL);
    listener_ = nullptr;
    return SUCCESS;
}

int PrinterTask::StopPrint()
{
    do {
        std::lock_guard<std::mutex> lock(mutex_);
        run_ = false;
        while (!queTasks_.empty()) {
            queTasks_.pop();
        }
    } while (false);

    thread_id_ = 0;
    return SUCCESS;
}

void PrinterTask::RunTask()
{
    std::unique_lock<std::mutex> lock(mutex_);
    run_ = true;
    int total = queTasks_.size();
    int num = 0;
    while (run_) {
        if (queTasks_.empty()) {
            status_ = ERR_PRINTER_NO_TASK;
            break;
        }
        // 停顿300ms
        std::this_thread::sleep_for(std::chrono::milliseconds(100));

        PrintItem task = queTasks_.front();
        queTasks_.pop();
        // 应允许添加新任务
        lock.unlock();
        switch (task.ppt) {
            case PT_TYPE_TEXT:
                DoTextTask(task);
                break;
            case PT_TYPE_BITMAP:
                DoBitmapTask(task);
                break;
            case PT_TYPE_QRCODE:
                DoQRTask(task);
                break;
            case PT_TYPE_BARCODE:
                DoBarcodeTask(task);
                break;
            default:
                OnFailed(ERR__PRINTER_DATA_PACKET_ERROR, "任务类型不支持");
                break;
        }
        OnSuccess(++num, total);
        lock.lock();
    }
    run_ = false;
    listener_ = nullptr;
}

void PrinterTask::DoTextTask(const PrintItem &task)
{
    RECPTPRINTER_HILOGD(RECPTPRINTER_PLUGIN,
        "x = %{public}d, y = %{public}d, align = %{public}d, size = %{public}d, content = %{public}s",
        task.x, task.y, task.fmt.align, task.fmt.size, task.content.c_str());
    // 字体大小
    if (task.fmt.size == PF_SIZE_EXTRASMALL) {
        POS_Control_SetFontSize(0x00);
    } else if (task.fmt.size == PF_SIZE_SMALL) {
        POS_Control_SetFontSize(0x10);
    } else if (task.fmt.size == PF_SIZE_MEDIUM) {
        POS_Control_SetFontSize(0x20);
    } else if (task.fmt.size == PF_SIZE_LARGE) {
        POS_Control_SetFontSize(0x20);
    } else if (task.fmt.size == PF_SIZE_EXTRALARGE) {
        POS_Control_SetFontSize(0x30);
    } else {
        POS_Control_SetFontSize(0x00);
    }

    // 粗体
    POS_Control_SetBold(task.fmt.bold);


    int ret = 0;
    std::string utf8txt = task.content;
    int max_size = utf8txt.length()*2+1;
    char *gbk = (char*)malloc(max_size);

    //  汉字必须转GBK编码
    size_t gbk_len = 0;
    if ((gbk_len = str_utf82gbk(gbk, utf8txt.c_str(), utf8txt.length())) == 0) {
        free(gbk);
        OnFailed(ERR__PRINTER_DATA_PACKET_ERROR, "数据格式不支持");
        return;
    }
    gbk[gbk_len] = '\0';

    // 位置偏移
    POS_Control_SetYPosition(task.y*8);
    if (task.x != 0)
        POS_Control_SetXPosition(task.x*8);
    else
        POS_Control_AlignType(task.fmt.align);

    ret = POS_Output_PrintString((unsigned char*)(gbk));

    if (ret != 0) {
        status_ = StatusConvert(ret);
        OnFailed(status_, "文本打印失败");
    }
    free(gbk);
    return;
}

void PrinterTask::DoBitmapTask(const PrintItem &task)
{
    // POS_Control_SetYPosition(task.y*8);
    
    int ret = POS_Output_PrintBmpDirect(task.x*8,task.fmt.align, task.content.c_str());
    if (ret != 0) {
        status_ = StatusConvert(ret);
        OnFailed(status_, "图片打印失败");
    }
}

void PrinterTask::DoQRTask(const PrintItem &task)
{
    // POS_Control_SetYPosition(task.y*8);
    // if (task.x != 0)
    //     POS_Control_SetXPosition(task.x*8);
    // else
    //     
    POS_Control_AlignType(task.fmt.align);

    int ret = POS_Output_PrintBar2code(0, task.fmt.width, 1, (unsigned char*)task.content.c_str());
    if (ret != 0) {
        status_ = StatusConvert(ret);
        OnFailed(status_, "二维码打印失败");
    }
}

void PrinterTask::DoBarcodeTask(const PrintItem &task)
{
    // POS_Control_SetYPosition(task.y*8);
    // if (task.x != 0)
    //     POS_Control_SetXPosition(task.x*8);
    // else
    //     
    POS_Control_AlignType(task.fmt.align);

    int ret = POS_Output_PrintBar1code(73, task.fmt.width, task.fmt.height, task.fmt.hriloc,
        (unsigned char*)(task.content.c_str()));
    if (ret != 0) {
        status_ = StatusConvert(ret);
        OnFailed(status_, "条形码打印失败");
    }
}

void PrinterTask::OnFailed(int code, const std::string& err) {
    if (listener_) {
        listener_->OnFailed(code, err);
    }
}

void PrinterTask::OnSuccess(int proc, int total) {
    if (listener_) {
        listener_->OnSuccess(proc, total);
    }
}

}  // namespace Finance
}  // namespace SwanLink
